import {
    Component,
    Input,
    OnInit,
    OnDestroy,
    ChangeDetectorRef, Output, EventEmitter,
} from '@angular/core';
import { Subscription , timer} from "rxjs";
import { finalize } from "rxjs/operators";
import { ScannerVo, ScanningResultService } from "../../../../lib/services";
import { ErrorHandler } from "../../../../lib/utils/error-handler";
import { ChannelService } from "../../../../lib/services/channel.service";
import {
    clone,
    CURRENT_BASE_HREF,
    DEFAULT_SUPPORTED_MIME_TYPE,
    VULNERABILITY_SCAN_STATUS,
    dbEncodeURIComponent
} from "../../../../lib/utils/utils";
import { ArtifactService } from "../../../../../ng-swagger-gen/services/artifact.service";
import { Artifact } from "../../../../../ng-swagger-gen/models/artifact";
import { NativeReportSummary } from "../../../../../ng-swagger-gen/models/native-report-summary";


const STATE_CHECK_INTERVAL: number = 3000; // 3s
const RETRY_TIMES: number = 3;

@Component({
    selector: 'hbr-vulnerability-bar',
    templateUrl: './result-bar-chart-component.html',
    styleUrls: ['./scanning.scss']
})
export class ResultBarChartComponent implements OnInit, OnDestroy {
    @Input() scanner: ScannerVo;
    @Input() repoName: string = "";
    @Input() projectName: string = "";
    @Input() artifactDigest: string = "";
    @Input() summary: NativeReportSummary;
    onSubmitting: boolean = false;
    retryCounter: number = 0;
    stateCheckTimer: Subscription;
    scanSubscription: Subscription;
    timerHandler: any;
    @Output()
    submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
    @Output()
    scanFinished: EventEmitter<Artifact> = new EventEmitter<Artifact>();

    constructor(
        private artifactService: ArtifactService,
        private scanningService: ScanningResultService,
        private errorHandler: ErrorHandler,
        private channel: ChannelService,
    ) { }

    ngOnInit(): void {
        if ((this.status === VULNERABILITY_SCAN_STATUS.RUNNING ||
            this.status === VULNERABILITY_SCAN_STATUS.PENDING) &&
            !this.stateCheckTimer) {
            // Avoid duplicated subscribing
            this.stateCheckTimer = timer(0, STATE_CHECK_INTERVAL).subscribe(() => {
                this.getSummary();
            });
        }
        this.scanSubscription = this.channel.scanCommand$.subscribe((artifactDigest: string) => {
            let myFullTag: string = this.repoName + "/" + this.artifactDigest;
            if (myFullTag === artifactDigest) {
                this.scanNow();
            }
        });
    }

    ngOnDestroy(): void {
        if (this.stateCheckTimer) {
            this.stateCheckTimer.unsubscribe();
            this.stateCheckTimer = null;
        }
        if (this.scanSubscription) {
            this.scanSubscription.unsubscribe();
        }
    }

    // Get vulnerability scanning status
    public get status(): string {
        if (this.summary && this.summary.scan_status) {
            return this.summary.scan_status;
        }
        return VULNERABILITY_SCAN_STATUS.NOT_SCANNED;
    }

    public get completed(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.SUCCESS;
    }

    public get error(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.ERROR;
    }

    public get queued(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.PENDING;
    }

    public get scanning(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.RUNNING;
    }
    public get otherStatus(): boolean {
        return !(this.completed || this.error || this.queued || this.scanning);
    }

    scanNow(): void {
        if (this.onSubmitting) {
            // Avoid duplicated submitting
            console.log("duplicated submit");
            return;
        }

        if (!this.repoName || !this.artifactDigest) {
            console.log("bad repository or tag");
            return;
        }

        this.onSubmitting = true;

        this.scanningService.startVulnerabilityScanning(this.projectName, dbEncodeURIComponent(this.repoName), this.artifactDigest)
            .pipe(finalize(() => this.submitFinish.emit(false)))
            .subscribe(() => {
                this.onSubmitting = false;
                // Forcely change status to queued after successful submitting
                this.summary = {
                    scan_status: VULNERABILITY_SCAN_STATUS.PENDING,
                };
                // Start check status util the job is done
                if (!this.stateCheckTimer) {
                    // Avoid duplicated subscribing
                    this.stateCheckTimer = timer(STATE_CHECK_INTERVAL, STATE_CHECK_INTERVAL).subscribe(() => {
                        this.getSummary();
                    });
                }
            }, error => {
                this.onSubmitting = false;
                if (error && error.error && error.error.code === 409) {
                     console.log(error.error.message);
                } else {
                     this.errorHandler.error(error);
                }
            });
    }

    getSummary(): void {
        if (!this.repoName || !this.artifactDigest) {
            return;
        }
        this.artifactService.getArtifact({
            projectName: this.projectName,
            repositoryName: dbEncodeURIComponent(this.repoName),
            reference: this.artifactDigest,
            withScanOverview: true
        })
            .subscribe((artifact: Artifact) => {
                // To keep the same summary reference, use value copy.
                if (artifact.scan_overview) {
                    this.copyValue(artifact.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]);
                }
                if (!this.queued && !this.scanning) {
                    // Scanning should be done
                    if (this.stateCheckTimer) {
                        this.stateCheckTimer.unsubscribe();
                        this.stateCheckTimer = null;
                    }
                    this.scanFinished.emit(artifact);
                }
                this.channel.ArtifactDetail$.next(artifact);
            }, error => {
                this.errorHandler.error(error);
                this.retryCounter++;
                if (this.retryCounter >= RETRY_TIMES) {
                    // Stop timer
                    if (this.stateCheckTimer) {
                        this.stateCheckTimer.unsubscribe();
                        this.stateCheckTimer = null;
                    }
                    this.retryCounter = 0;
                }
            });
    }

    copyValue(newVal: NativeReportSummary): void {
        if (!this.summary || !newVal || !newVal.scan_status) { return; }
        this.summary = clone(newVal);
    }
    viewLog(): string {
        return  `${ CURRENT_BASE_HREF }/projects/${this.projectName
        }/repositories/${dbEncodeURIComponent(this.repoName)}/artifacts/${this.artifactDigest}/scan/${this.summary.report_id}/log`;
    }
}
